This section describes a sample sequence grabber channel component for PICT image data.
Listing 6-1 supplies the component dispatchers for the sequence grabber channel component together with the required functions.
Listing 1 Setting up global variables and implementing required functions
#define kMediaTimeScale 600
typedef struct {
ComponentInstance self;
SeqGrabComponent grabber;
long usage;
Boolean paused;
CGrafPtr destPort;
GDHandle destGD;
CGrafPort tempPort;
MatrixRecord displayMatrix;
Rect destRect;
Rect srcRect;
RgnHandle clip;
Boolean inPreview;
Boolean inRecord;
TimeBase base;
long bytesWritten;
Boolean showTickCount;
long saveUsage;
} SGPictGlobalsRecord, *SGPictGlobals;
pascal ComponentResult SGPICTDispatcher
(ComponentParameters *params, Handle storage)
{
OSErr err = badComponentSelector;
ComponentFunction componentProc = 0;
switch (params->what) {
case kComponentOpenSelect:
componentProc = SGPictOpen; break;
case kComponentCloseSelect:
componentProc = SGPictClose; break;
case kComponentCanDoSelect:
componentProc = SGPictCanDo; break;
case kComponentVersionSelect:
componentProc = SGPictVersion; break;
case kSGSetGWorldSelect:
componentProc = SGPictSetGWorld; break;
case kSGStartPreviewSelect:
componentProc = SGPictStartPreview; break;
case kSGStartRecordSelect:
componentProc = SGPictStartRecord; break;
case kSGIdleSelect:
componentProc = SGPictIdle; break;
case kSGStopSelect:
componentProc = SGPictStop; break;
case kSGPauseSelect:
componentProc = SGPictPause; break;
case kSGPrepareSelect:
componentProc = SGPictPrepare; break;
case kSGReleaseSelect:
componentProc = SGPictRelease; break;
case kSGCSetChannelUsageSelect:
componentProc = SGPictSetChannelUsage; break;
case kSGCGetChannelUsageSelect:
componentProc = SGPictGetChannelUsage; break;
case kSGCSetChannelBoundsSelect:
componentProc = SGPictSetChannelBounds; break;
case kSGCGetChannelBoundsSelect:
componentProc = SGPictGetChannelBounds; break;
case kSGCGetChannelInfoSelect:
componentProc = SGPictGetChannelInfo; break;
case kSGCSetChannelMatrixSelect:
componentProc = SGPictSetChannelMatrix; break;
case kSGCGetChannelMatrixSelect:
componentProc = SGPictGetChannelMatrix; break;
case kSGCSetChannelClipSelect:
componentProc = SGPictSetChannelClip; break;
case kSGCGetChannelClipSelect:
componentProc = SGPictGetChannelClip; break;
case kSGCGetChannelSampleDescriptionSelect:
componentProc = SGPictGetChannelSampleDescription;
break;
case kSGCGetChannelDeviceListSelect:
componentProc = SGPictGetChannelDeviceList; break;
case kSGCSetChannelDeviceSelect:
componentProc = SGPictSetChannelDevice; break;
case kSGCGetChannelTimeScaleSelect:
componentProc = SGPictGetChannelTimeScale; break;
case kSGCInitChannelSelect:
componentProc = SGPictInitChannel; break;
case kSGCWriteSamplesSelect:
componentProc = SGPictWriteSamples; break;
case kSGCGetDataRateSelect:
componentProc = SGPictGetDataRate; break;
case kSGCPanelGetDitlSelect:
componentProc = SGPictPanelGetDitl; break;
case kSGCPanelInstallSelect:
componentProc = SGPictPanelInstall; break;
case kSGCPanelEventSelect:
componentProc = SGPictPanelEvent; break;
case kSGCPanelRemoveSelect:
componentProc = SGPictPanelRemove; break;
case kSGCPanelGetSettingsSelect:
componentProc = SGPictPanelGetSettings; break;
case kSGCPanelSetSettingsSelect:
componentProc = SGPictPanelSetSettings; break;
case 0x0100:
componentProc = SGPictSetShowTickCount; break;
case 0x0101:
componentProc = SGPictGetShowTickCount; break;
}
if (componentProc)
err = CallComponentFunctionWithStorage (storage, params,
componentProc);
return err;
}
pascal ComponentResult SGPictCanDo (SGPictGlobals store,
short ftnNumber)
{
switch (ftnNumber) {
case kComponentOpenSelect:
case kComponentCloseSelect:
case kComponentCanDoSelect:
case kComponentVersionSelect:
case kSGSetGWorldSelect:
case kSGStartPreviewSelect:
case kSGStartRecordSelect:
case kSGIdleSelect:
case kSGStopSelect:
case kSGPauseSelect:
case kSGPrepareSelect:
case kSGReleaseSelect:
case kSGCSetChannelUsageSelect:
case kSGCGetChannelUsageSelect:
case kSGCSetChannelBoundsSelect:
case kSGCGetChannelBoundsSelect:
case kSGCGetChannelInfoSelect:
case kSGCSetChannelMatrixSelect:
case kSGCGetChannelMatrixSelect:
case kSGCSetChannelClipSelect:
case kSGCGetChannelClipSelect:
case kSGCGetChannelSampleDescriptionSelect:
case kSGCGetChannelDeviceListSelect:
case kSGCSetChannelDeviceSelect:
case kSGCGetChannelTimeScaleSelect:
case kSGCInitChannelSelect:
case kSGCWriteSamplesSelect:
case kSGCGetDataRateSelect:
case kSGCPanelGetDitlSelect:
case kSGCPanelInstallSelect:
case kSGCPanelEventSelect:
case kSGCPanelRemoveSelect:
case kSGCPanelGetSettingsSelect:
case kSGCPanelSetSettingsSelect:
/* private component functions */
case 0x0100:
case 0x0101:
return true;
default:
return false;
}
}
pascal ComponentResult SGPictVersion (SGPictGlobals store)
{
return 0x00020001;
}
pascal ComponentResult SGPictOpen (SGPictGlobals store,
ComponentInstance self)
{
OSErr err;
GrafPtr savePort;
/* allocate global variables */
store =
(SGPictGlobals)NewPtrClear(sizeof(SGPictGlobalsRecord));
if (err = MemError()) goto bail;
/* create a temporary port for drawing during the idle
function */
GetPort (&savePort);
OpenCPort (&store->tempPort);
SetPort ((GrafPtr)&store->tempPort);
PortSize (4096, 4096);
SetRectRgn (store->tempPort.visRgn, 0, 0, 4096, 4096);
ClipRgn (store->tempPort.visRgn);
SetPort (savePort);
store->self = self;
store->showTickCount = false;
SetComponentInstanceStorage (self, (Handle)store);
bail:
return err;
}
pascal ComponentResult SGPictClose (SGPictGlobals store,
ComponentInstance self)
{
/* disposal operations */
if (store) {
if (store->clip) DisposeRgn(store->clip);
CloseCPort(&store->tempPort);
DisposPtr((Ptr)store);
}
return noErr;
}
To initialize the channel component, the sequence grabber component calls the SGInitChannel function, which is described on SGInitChannel .
The code in Listing 6-2 initializes channel variables. The grabber component calls the SGPictInitChannel function to initialize a sequence grabber channel component. The SGPictInitChannel function calls QuickDraw's SetRect routine and QuickTime's SetIdentityMatrix function to specify the size of the area (around a mouse-down event) in which the sequence grabber component will capture PICT images. For more on the SetRect routine, see the chapter "Basic QuickDraw" in Inside Macintosh: Imaging . For details on the SetIdentityMatrix function, see the chapter "Movie Toolbox" in Inside Macintosh: QuickTime .
Listing 2Initializing the sequence grabber channel component
pascal ComponentResult SGPictInitChannel (SGPictGlobals store,
SeqGrabComponent owner)
{
/* initialize any variables here */
SetRect(&store->srcRect, 0, 0, 160, 120);/* rectangle in which
capture occurs */
SetIdentityMatrix (&store->displayMatrix);
store->grabber = owner;
SGGetTimeBase (owner, &store->base);
return noErr;
}
Listing 6-3 supplies configuration functions that set the usage parameters and storage for the channel component. (See the descriptions of the SGSetChannelUsage and SGGetChannelUsage functions on SGSetChannelUsage and SGGetChannelUsage , respectively, for details.)
The sample code illustrates how to retrieve usage information. (See the description of the SGGetChannelInfo function on SGGetChannelInfo for details.) In this case, you indicate that the sequence grabber component has spatial boundaries by using the seqGrabHasBounds constant in the channelInfo parameter.
Listing 3 Determining usage parameters and getting usage data
pascal ComponentResult SGPictSetChannelUsage(SGPictGlobals store,
long usage)
{
/* remember usage */
store->usage = usage;
return noErr;
}
pascal ComponentResult SGPictGetChannelUsage(SGPictGlobals store,
long *usage)
{
/* return usage */
*usage = store->usage;
return noErr;
}
pascal ComponentResult SGPictGetChannelInfo (SGPictGlobals store,
long *channelInfo)
{
/* indicate that you have spatial boundaries */
*channelInfo = seqGrabHasBounds;
return noErr;
}
To set up an area in which the channel component displays image data, the sequence grabber should perform these tasks:
The code in Listing 6-4 provides an example of how to manage the spatial characteristics of the area in which the channel component displays PICT image data.
Listing 4 Managing spatial characteristics
pascal ComponentResult SGPictSetGWorld (SGPictGlobals store,
CGrafPtr gp, GDHandle gd)
{
/* remember the destination graphics world */
store->destPort = gp;
store->destGD = gd;
return noErr;
}
pascal ComponentResult SGPictSetChannelMatrix
(SGPictGlobals store, const MatrixRecord *m)
{
OSErr err = noErr;
MatrixRecord mat;
short matType;
/* determine the matrix being set */
if (m)
mat = *m;
else
SetIdentityMatrix (&mat);
/* validate it */
matType = GetMatrixType (&mat);
if ((mat.matrix[0][0] < 0) || (mat.matrix[1][1] < 0) ||
(matType >= linearMatrixType))
return paramErr;
/* update the matrix and destination rectangle */
store->displayMatrix = mat;
store->destRect = store->srcRect;
TransformRect (&mat, &store->destRect, nil);
return err;
}
pascal ComponentResult SGPictGetChannelMatrix
(SGPictGlobals store, MatrixRecord *m)
{
/* return current matrix */
*m = store->displayMatrix;
return noErr;
}
pascal ComponentResult SGPictSetChannelBounds
(SGPictGlobals store, const Rect *bounds)
{
/* remember destination rect */
store->destRect = *bounds;
/* recalculate display matrix from it */
RectMatrix (&store->displayMatrix, &store->srcRect,
&store->destRect);
return noErr;
}
pascal ComponentResult SGPictGetChannelBounds
(SGPictGlobals store, Rect *bounds)
{
/* return current boundaries */
*bounds = store->destRect;
return noErr;
}
pascal ComponentResult SGPictSetChannelClip (SGPictGlobals store,
RgnHandle theClip)
{
OSErr err = noErr;
/* toss the old channel clipping */
if (store->clip) {
DisposeRgn (store->clip);
store->clip = nil;
}
/* and remember the new one */
if (theClip) {
err = HandToHand ((Handle *)&theClip);
store->clip = theClip;
}
return err;
}
pascal ComponentResult SGPictGetChannelClip
(SGPictGlobals store, RgnHandle *theClip)
{
OSErr err = noErr;
/* return clip, if there is one */
if (*theClip = store->clip)
err = HandToHand ((Handle *)theClip);
return err;
}
To preview and record image data in the channel component, the code in Listing 6-5 implements these tasks:
The code in Listing 6-5 illustrates a channel component's control of the previewing and recording of a PICT image.
Listing 5 Controlling previewing and recording operations
pascal ComponentResult SGPictStartPreview (SGPictGlobals store)
{
/* into preview mode */
store->inPreview = (store->usage & seqGrabPreview) != 0;
return noErr;
}
pascal ComponentResult SGPictStartRecord (SGPictGlobals store)
{
/* into record mode (also preview, if PlayDuringRecord) */
store->inRecord = (store->usage & seqGrabRecord) != 0;
store->inPreview = (store->usage & seqGrabPlayDuringRecord) !=
0;
return noErr;
}
pascal ComponentResult SGPictIdle (SGPictGlobals store)
{
OSErr err = noErr;
/* this is where the work for preview and record happens */
if (!store->paused && (store->inRecord || store->inPreview)) {
Point mouseLoc;
Rect r;
PicHandle tempPict = nil;
TimeRecord tr;
CGrafPtr savePort;
GDHandle saveGD;
Rect maxR;
GetGWorld (&savePort, &saveGD);
/* get the current time */
GetTimeBaseTime (store->base, kMediaTimeScale, &tr);
/* figure the current area around the mouse
(only on main screen) */
SetGWorld (&store->tempPort, GetMainDevice());
GetMouse (&mouseLoc);
LocalToGlobal (&mouseLoc);
r.top = r.bottom = mouseLoc.v;
r.left = r.right = mouseLoc.h;
InsetRect(&r, -(store->srcRect.right >> 1),
-(store->srcRect.bottom >> 1));
maxR = (**GetMainDevice()).gdRect;
if (r.left < maxR.left)
OffsetRect (&r, -r.left + maxR.left, 0);
if (r.top < maxR.top)
OffsetRect (&r, 0, -r.top + maxR.top);
if (r.right > maxR.right)
OffsetRect(&r, maxR.right - r.right, 0);
if (r.bottom > maxR.bottom)
OffsetRect (&r, 0, maxR.bottom - r.bottom);
/* copy the screen into a picture */
tempPict = OpenPicture(&r);
CopyBits ((BitMap *)&store->tempPort.portPixMap,
(BitMap *)&store->tempPort.portPixMap, &r, &r,
srcCopy, nil);
if (store->showTickCount) {
/* if users want to see ticks, draw them */
Str63 str;
NumToString ( TickCount(), str);
/* do some magic positioning */
r.right = r.left + StringWidth(str) + 4;
r.bottom = r.top + 14;
EraseRect (&r);
MoveTo(r.left + 2, r.bottom - 3);
TextSize (12);
DrawString (str);
}
ClosePicture();
/* if recording, add data to movie */
if (store->inRecord) {
long offset;
long pictSize = GetHandleSize ((Handle)tempPict);
HLock ((Handle)tempPict);
err = SGAddMovieData (store->grabber, store->self,
(Ptr)*tempPict, pictSize, &offset, 0,
tr.value.lo, seqGrabWriteAppend);
store->bytesWritten += pictSize;
}
/* if you need to show the preview image, do that */
if (store->inPreview) {
RgnHandle saveClip;
SetGWorld (store->destPort, store->destGD);
if (store->clip) {
saveClip = NewRgn();
GetClip (saveClip);
SetClip (store->clip);
}
DrawPicture (tempPict, &store->destRect);
if (store->clip) {
SetClip (saveClip);
DisposeRgn (saveClip);
}
}
KillPicture (tempPict);
SetGWorld (savePort, saveGD);
}
return err;
}
pascal ComponentResult SGPictStop (SGPictGlobals store)
{
/* stop all previewing and recording */
store->inRecord = store->inPreview = false;
return noErr;
}
pascal ComponentResult SGPictPause (SGPictGlobals store,
Byte pause)
{
/* pause */
store->paused = pause;
return noErr;
}
pascal ComponentResult SGPictPrepare (SGPictGlobals store,
Boolean prepareForPreview,
Boolean prepareForRecord)
{
/* prepare for previewing and recording operations--
all you do here is initialize a variable */
store->bytesWritten = 0;
return noErr;
}
pascal ComponentResult SGPictRelease (SGPictGlobals store)
{
/* no resources to release after previewing or recording */
return noErr;
}
To manage channel devices such as video digitizers or sound input drivers, you should
Listing 6-6 provides examples of these required functions for channel device management. The SGPictGetChannelDeviceList function obtains a list of devices associated with the channel component. The SGPictSetChannelDevice function allows the sequence grabber to specify a channel device. In this code sample, there are no devices associated with the channel component.
Listing 6 Coordinating devices for the channel component
pascal ComponentResult SGPictGetChannelDeviceList
(SGPictGlobals store,
long selectionFlags,
SGDeviceList *list)
{
*list = (SGDeviceList) NewHandleClear
(sizeof (SGDeviceListRecord)); /* no devices */
return MemError();
}
pascal ComponentResult SGPictSetChannelDevice
(SGPictGlobals store, StringPtr name)
{
/* you have no devices, so no problem */
return noErr;
}
To record image data, the channel component must allow the sequence grabber to do the following:
The code in Listing 6-7 shows how the channel component uses these utility functions to record PICT image data.
pascal ComponentResult SGPictGetChannelTimeScale
(SGPictGlobals store, TimeScale *scale)
{
*scale = kMediaTimeScale; /* a reasonable default time scale */
return noErr;
}
pascal ComponentResult SGPictGetChannelSampleDescription
(SGPictGlobals store, Handle sampleDesc)
{
OSErr err;
SampleDescriptionPtr sdp;
SetHandleSize (sampleDesc, sizeof(SampleDescription));
if (err = MemError()) goto bail;
/* make up a minimal sample description */
sdp = (SampleDescriptionPtr)*sampleDesc;
sdp->descSize = sizeof(SampleDescription);
sdp->dataFormat = 'PICT';
sdp->resvd1 = 0;
sdp->resvd2 = 0;
sdp->dataRefIndex = 0;
bail:
return err;
}
pascal ComponentResult SGPictWriteSamples (SGPictGlobals store,
Movie m, AliasHandle theFile)
{
OSErr err = 0;
Track pictT;
Media pictM;
long i;
MatrixRecord aMatrix;
Rect from, to;
seqGrabFrameInfo fi;
TimeRecord tr;
TimeValue mediaDuration;
SampleDescriptionHandle sampleDesc = 0;
/* after SGStop, this function creates the track and media */
if (!(store->usage & seqGrabRecord))
return err;
/* get the sample description */
sampleDesc = (SampleDescriptionHandle)NewHandle(4);
if (err = MemError()) goto bail;
if (err = SGGetChannelSampleDescription (store->self,
(Handle)sampleDesc)) goto bail;
/* figure out the track matrix */
SetRect (&from, 0, 0, store->srcRect.right,
store->srcRect.bottom);
to = from;
TransformRect (&store->displayMatrix, &to, nil);
/* create the track and media */
pictT = NewMovieTrack (m, (long)from.right << 16,
(long)from.bottom << 16, 0);
pictM = NewTrackMedia (pictT, 'PICT', kMediaTimeScale,
(Handle)theFile, rAliasType);
/* spin in a loop getting sample references from the
sequence grabber and adding them to the media */
fi.frameChannel = store->self;
i = -1;
do {
TimeValue frameDuration;
err = SGGetNextFrameReference (store->grabber,
&fi, &frameDuration, &i);
if (err) {
if (err == paramErr)
err = 0;
break;
}
err = AddMediaSampleReference (pictM,
fi.frameOffset, fi.frameSize,
frameDuration,
sampleDesc, 1,
0, 0);
if (err == invalidDuration) {
err = noErr;
break;
}
} while (!err);
done:
if (err) goto bail;
GetTimeBaseTime (store->base, 0, &tr);
ConvertTimeScale (&tr, kMediaTimeScale);/* trim media inserted
to not extend
beyond end time */
mediaDuration = GetMediaDuration(pictM);
/* add media to track */
err = InsertMediaIntoTrack (pictT, 0, 0, tr.value.lo, kFix1);
/* set track matrix */
RectMatrix (&aMatrix, &from, &to);
SetTrackMatrix (pictT, &aMatrix);
/* set track clipping region */
SetTrackClipRgn (pictT, store->clip);
bail:
if (sampleDesc) DisposHandle ((Handle)sampleDesc);
return err;
}
pascal ComponentResult SGPictGetDataRate (SGPictGlobals store,
long *bytesPerSecond)
{
/* take a guess at the data rate */
*bytesPerSecond = 24 * 1024;
if (store->bytesWritten) {
TimeValue timeNow = GetTimeBaseTime (store->base, 8, nil);
/* one-eighth second resolution */
if (!timeNow)
return seqGrabInfoNotAvailable;
*bytesPerSecond = (store->bytesWritten / timeNow) * 8;
/* convert back to seconds */
}
return noErr;
}
The channel can provide media-specific functions for a particular channel type. These functions are analogous to the SGSetVideoCompressorType and SGGetVideoCompressorType functions (described on SGSetVideoCompressorType and SGGetVideoCompressorType , respectively). These functions allow the sequence grabber to specify and determine the type of image compression the channel component is to apply to the captured video images.
The code in Listing 6-8 provides two specialized channel component functions, SGPictSetShowTickCount and SGPictGetShowTickCount , which set and retrieve the tick count, respectively. Note that both the functions refer to the showTickCount field in the SGPictGlobals structure.
Listing 8Showing the tick count
pascal ComponentResult SGPictSetShowTickCount
(SGPictGlobals store, Boolean show)
{
store->showTickCount = show;
return noErr;
}
pascal ComponentResult SGPictGetShowTickCount
(SGPictGlobals store, Boolean *show)
{
*show = store->showTickCount;
return noErr;
}
The channel allows the sequence grabber to manage the placement of your channel data in the sequence grabber's settings dialog box.
For details on the SGPanelGetDITL , SGPanelInstall , SGPanelEvent , and SGPanelRemove functions, see the chapter "Sequence Grabber Panel Components" in this book.
The code in Listing 6-9 calls the sequence grabber panel component and indicates that the channel component will display a tick count checkbox in the panel settings.
Listing 9Including a tick count checkbox in a dialog box in the panel component
pascal ComponentResult SGPictPanelGetDitl (SGPictGlobals store,
Handle *ditl)
{
/* get and detach your dialog template */
*ditl = GetResource('DITL', 7000);
if (!*ditl) return resNotFound;
DetachResource(*ditl);
return noErr;
}
pascal ComponentResult SGPictPanelInstall (SGPictGlobals store,
SGChannel c,
DialogPtr d,
short itemOffset)
{
Rect newBounds;
short kind;
Handle h;
/* reset this channel to use the dialog window and be in
preview mode with no clip */
SGSetGWorld (store->self, (CGrafPtr)d, GetMainDevice());
SGGetChannelUsage (store->self, &store->saveUsage);
SGSetChannelUsage (store->self, seqGrabPreview);
SGSetChannelClip (c, nil);
/* update boundaries to match size of user item */
GetDItem (d, 1 + itemOffset, &kind, &h, &newBounds);
SGSetChannelBounds (c, &newBounds);
SGStartPreview (store->self);
return noErr;
}
pascal ComponentResult SGPictPanelEvent (SGPictGlobals store,
SGChannel c, DialogPtr d,
short itemOffset,
EventRecord *theEvent,
short *itemHit,
Boolean *handled)
{
/* use idle time to draw */
if (theEvent->what == nullEvent)
return SGIdle (store->self);
return noErr;
}
pascal ComponentResult SGPictPanelRemove (SGPictGlobals store,
SGChannel c, DialogPtr d,
short itemOffset)
{
/* stop playing */
SGStop (store->self);
SGRelease (store->self);
/* note that the clip and bounds are automatically restored
for you because you stored them using the SGGetSettings
function */
/* restore usage */
SGSetChannelUsage(store->self, store->saveUsage);
return noErr;
}
The final step in the implementation of a sequence grabber channel component is the display of the channel preview in the settings dialog box. Two sequence grabber functions, SGSettingsDialog and SGGetSettingsDialog (described in the chapter "Sequence Grabber Components" in this book), facilitate this process.
Listing 6-10 provides code that creates a user data list to contain the tick count information for the sequence grabber's settings dialog box, adds a matrix to the list, and stores clipping information (if any exists). The sample code then restores the clipping and the matrix.
Listing 10Displaying channel settings
pascal ComponentResult SGPictPanelGetSettings
(SGPictGlobals store, SGChannel c,
UserData *result, long flags)
{
OSErr err = noErr;
UserData ud = 0;
MatrixRecord matrix;
RgnHandle clip;
/* create a user data list to hold your state */
if (err = NewUserData (&ud)) goto bail;
/* add matrix to user data */
if (SGGetChannelMatrix (c, &matrix) == noErr) {
if (err = SetUserDataItem (ud, &matrix, sizeof(matrix),
sgMatrixType, 1))
goto bail;
}
/* store clip, if there is one */
if (SGGetChannelClip (c, &clip) == noErr) {
if (clip)
err = AddUserData (ud, (Handle)clip, sgClipType);
else
err = SetUserDataItem (ud, nil, 0, sgClipType, 1);
/* add a dummy to indicate none */
DisposeRgn(clip);
if (err) goto bail;
}
bail:
if (err) {
DisposeUserData (ud);
ud = 0;
}
*result = ud;
return err;
}
pascal ComponentResult SGPictPanelSetSettings
(SGPictGlobals store,
SGChannel c, UserData ud, long flags)
{
OSErr err;
RgnHandle clip = NewRgn();
MatrixRecord matrix;
/* restore clip, if one was stored */
if (GetUserData (ud, (Handle)clip, sgClipType, 1) == noErr) {
if (err = SGSetChannelClip
(c, GetHandleSize ((Handle)clip) ? clip : 0))
goto bail;
}
/* restore matrix */
if (err = GetUserDataItem (ud, &matrix, sizeof(matrix),
sgMatrixType, 1)) goto bail;
if (err = SGSetChannelMatrix (c, &matrix))
goto bail;
bail:
DisposeRgn (clip);
return err;
}